package wangjg.mda.lang;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;

import wangjg.mda.model.obj.ModelDataType;

//@SuppressWarnings("unused")
public class CodeParser extends CommentParser {

	private static final String REG_RESOLVED="true|false|null|abstract|assert|break|case|catch|class|const|continue|default|do|else|enum|extends|final|finally|for|goto|if|implements|import|instanceof|interface|native|new|package|private|protected|public|return|static|strictfp|super|switch|synchronized|this|throw|throws|transient|try|void|volatile|while";
	private static final String REG_KEYWORD="[a-z]+";
//	private static final String REG_TYPE="\\w+";
	private static final String REG_TYPE="\\w+(?:<[^>]+>)?";
	private static final String REG_VARIABLE="[a-z_A-Z\\$][a-z_A-Z\\$<>]*";
	private static final String REG_ANNO="@("+REG_TYPE+")\\s*(?:\\(\\s*([^\\)]*)\\s*\\))?\\s+";
	private static final String REG_ANNOS="((?:@"+REG_TYPE+"(?:\\([^\\)]*\\))?\\s+)*)";
	private static final String REG_MODIFIER = "(?:(?:public|private|protected|static|final|abstract)\\s+)";
	private static final String REG_FILED = REG_ANNOS+"("+REG_MODIFIER+"+)"+"("+REG_TYPE+")\\s+("+REG_IDENTIFIERS+")\\s*(?:=\\s*([^;\\n]+)\\s*)?;";
//	private static final String REG_FILED = "private\\s+("+REG_TYPE+")\\s+("+REG_IDENTIFIERS+")(\\s*=\\s*\\S+\\s*)?;";

	private static final String REG_IMPORT = "import\\s*([^;]+)\\s*;";
	private static final String REG_CLASS="(?:^|\\s)"+REG_ANNOS+"(?:(public|private)\\s+)?(final\\s+)?(class|interface)\\s+("+REG_IDENTIFIERS+")";
	private static final String _REG_IMPLEMENT_CLASS = "[a-zA-Z0-9\\$\\.]+";//较准确的：[a-z_A-Z\\$](?:[a-z_A-Z\\$\\.]*[a-z_A-Z\\$])?
	private static final String REG_IMPLEMENT="(\\s+implements\\s+((?:"+_REG_IMPLEMENT_CLASS+"\\s*,?\\s*)+))?";
	private static final String REG_CLASS2=REG_CLASS+"(?:\\s+extends\\s+("+REG_IDENTIFIERS+"))?"+REG_IMPLEMENT+"\\s*\\{";
	private static final String REG_PACKAGE="^\\s*package\\s+([a-z_A-Z\\$](.?\\w)*);";

	//private void parseComment(MyComment comment) throws Exception,Exception{
	private static final String REG_FUNC_PARAM = REG_ANNOS+"("+REG_KEYWORD+"\\s+)?("+REG_VARIABLE+")\\s+("+REG_VARIABLE+")\\s*(?:,|$)?\\s*";
	private static final String REG_FUNC_PARAMS = "(\\s*(?:"+REG_FUNC_PARAM+")*)";
	private static final String REG_FUNC_THROW = "("+REG_IDENTIFIERS+")\\s*[,|\\$]?\\s*";
	private static final String REG_FUNC_THROWZ = "\\s*(?:throws\\s+((?:"+REG_FUNC_THROW+")+))?";
	private static final String REG_FUNC = "(?:^|\\s)"+REG_ANNOS+"("+REG_MODIFIER+"*)(?:("+REG_VARIABLE+")\\s+)?("+REG_IDENTIFIERS+")\\s*\\("+REG_FUNC_PARAMS+"\\)"+REG_FUNC_THROWZ+"\\s*[;|\\{]";
	protected static final String REG_KEYVALUE="\\s*(\\w+?)\\s*=\\s*(([\\\"'])([^\\\"']+?)\\3|([^,\\(\\)]+))\\s*(?:,|$)?";

	static String noGroup(String regex){
		regex = regex.replaceAll("\\\\\\(", "##");
		regex = regex.replaceAll("\\(\\?\\:", "==");
		regex = regex.replaceAll("\\(", "(?:");
		regex = regex.replaceAll("==", "(?:");
		regex = regex.replaceAll("##", "\\(");
		return regex;
	}
	
	//inner
	private List<MyField> fieldList = null;
	private MyPackage myPackage;
	private List<MyMethod> methodList = null;
	private MyClass cls;

	public static void main(String args[]){
		CodeParser cp = new CodeParser();
		cp.sourceCode = "class A{public A(){}; private short f(@A int id, int n){reutrn 1;}}";
		cp.getMyMethodList();
	}

	public MyClass getMyClass(){
		if(this.cls!=null){
			return this.cls;
		}
		Matcher matcher = this.getMatcher(sourceCode, REG_CLASS2);
		while(matcher.find()){
			MyClass cls = new MyClass();
			int blockStartPos = matcher.start(1);
			int nameBlockStartPos = matcher.start(5);
			String annosCode = matcher.group(1);
			List<MyAnnotation> annoList = this.getMyAnnotation(annosCode);
			String className = matcher.group(5);
			String superClass = matcher.group(6);
			cls.setName(className);
			cls.setSuperClass(superClass);
			cls.setPackage(this.getMyPackage());
			cls.setBlockStartPos(blockStartPos);
			cls.setNamePos(nameBlockStartPos);
			cls.setAnnos(annoList);
			
			cls.setComment(getMyClassComment(cls));
			
			cls.setImports(this.getMyImportList());
			
			cls.setFields(this.getMyFieldList());
			
			cls.setMethods(this.getMyMethodList());
			
			this.cls = cls;
			break;
		}
		return this.cls;
	}

	MyComment getMyClassComment(MyClass cls){
		int nameLineNo = this.getLineNumber(cls.getNamePos());
		MyComment comment = getCommentMap().get(nameLineNo);
		if(comment==null){
			comment = getCommentMap().get(nameLineNo-1);
		}
		if(comment==null){
			int blockLineNo = this.getLineNumber(cls.getBlockStartPos());
			comment = getCommentMap().get(blockLineNo-1);
		}
		return comment;
	}
	
	MyComment getMyFieldComment(MyField field, int startPos ,int endPos){
		int nameLineNo = this.getLineNumber(field.getNamePos());
		MyComment comment = getCommentMap().get(nameLineNo);
		if(comment==null){
			comment = getCommentMap().get(nameLineNo-1);
		}
		if(comment==null){
			int fieldBlockLineNo = this.getLineNumber(field.getBlockStartPos());
			comment = getCommentMap().get(fieldBlockLineNo-1);	
		}
		if(comment!=null){
			int commentLineNo = this.getLineNumber(comment.getBlockStartPos());
			int sartLineNo = this.getLineNumber(startPos);
			int endLineNo = this.getLineNumber(endPos);
			if(endLineNo==-1)endLineNo=Integer.MAX_VALUE;
			if(commentLineNo>sartLineNo && commentLineNo<endLineNo){
				return comment;
			}
		}
		return null;
	}
	
	MyComment getMyMethodComment(MyMethod myMethod){
		int nameLineNo = this.getLineNumber(myMethod.getNamePos());
		MyComment comment = getCommentMap().get(nameLineNo);
		if(comment==null){
			comment = getCommentMap().get(nameLineNo-1);
		}
		if(comment==null){
			int fieldBlockLineNo = this.getLineNumber(myMethod.getBlockStartPos());
			comment = getCommentMap().get(fieldBlockLineNo-1);	
		}
		return comment;
	}
	
	private MyPackage getMyPackage(){
		if(myPackage!=null){
			return myPackage;
		}
		Matcher matcher = this.getMatcher(sourceCode, REG_PACKAGE);
		while(matcher.find()){
			myPackage = new MyPackage();
			String name = matcher.group(1);
			myPackage.setName(name);
			myPackage.setBlockStartPos(matcher.start(0));
			myPackage.setBlockEndPos(matcher.end(0));
			return myPackage;
		}
		return null;
	}

	private List<MyField> getMyFieldList(){
		if(fieldList!=null){
			return fieldList;
		}
		fieldList = new ArrayList<MyField>();
		Matcher matcher = this.getMatcher(sourceCode, REG_FILED);
		while(matcher.find()){
			int blockStartPos = matcher.start();
			int blockEndPos = matcher.end();
			int namePos = matcher.start(4);
			String annosCode = matcher.group(1);
			String modifiers = matcher.group(2);
			if(modifiers!=null){modifiers=modifiers.trim();}
			String type = matcher.group(3);
			String name = matcher.group(4);
			String value = matcher.group(5);
			
			MyField field = new MyField();
			field.setBlockStartPos(blockStartPos);
			field.setBlockEndPos(blockEndPos);
			field.setNamePos(namePos );
			field.setAnnos(this.getMyAnnotation(annosCode));
			field.setModifiers(modifiers);
			field.setType(type);
			field.setName(name);
			field.setDefaultValue(value);
			this.fieldList.add(field);
		}
		//设置注释
		for(int i=0;i<fieldList.size();i++){
			//设置有效边界(不可靠)
			int startPos = 0;
			int endPos = Integer.MAX_VALUE;
			if(i>0){
				startPos = fieldList.get(i-1).getBlockEndPos();
			}
			if(i<fieldList.size()-1){
				endPos = fieldList.get(i+1).getBlockStartPos();
			}
			MyField field = fieldList.get(i);
			field.setComment(getMyFieldComment(field, startPos, endPos));
		}
		
		return fieldList;
	}
	
	private List<MyMethod> getMyMethodList(){
		if(this.methodList!=null){
			return methodList;
		}
		methodList = new ArrayList<MyMethod>();
		Matcher matcher = this.getMatcher(sourceCode, REG_FUNC);
		while(matcher.find()){
			this._printMatcher(matcher);
			MyMethod myMethod = new MyMethod();
			String modifiers = matcher.group(2);
			if(modifiers!=null){
				modifiers = modifiers.trim();
			}
			myMethod.setModifiers(modifiers);
			myMethod.setReturnType(matcher.group(3));
			myMethod.setName(matcher.group(4));
			myMethod.setAnnos(this.getMyAnnotation(matcher.group(1)));
			myMethod.setParameters(this.getMyParameterList(matcher.group(5)));
			myMethod.setThrowz(this.getMyThrowList(matcher.group(10)));
			myMethod.setBlockStartPos(matcher.start(1));
			myMethod.setNamePos(matcher.start(4));
			methodList.add(myMethod);
		}
		
		//设置注释
		for(int i=0;i<methodList.size();i++){
			MyMethod myMethod = methodList.get(i);
			myMethod.setComment(this.getMyMethodComment(myMethod));
		}
		
		return methodList;
	}
	
	private List<String> getMyThrowList(String code) {
		if(code==null)code="";
		List<String> list = new ArrayList<String>();
		Matcher matcher = this.getMatcher(code, REG_FUNC_THROW);
		while(matcher.find()){
			list.add(matcher.group(1));
		}
		return list;
	}

	private List<MyParameter> getMyParameterList(String code) {
		if(code==null)code="";
		List<MyParameter> list = new ArrayList<MyParameter>();
		Matcher matcher = this.getMatcher(code, REG_FUNC_PARAM);
		while(matcher.find()){
			this._printMatcher(matcher);
			MyParameter param = new MyParameter();
			param.setName(matcher.group(4));
			param.setType(matcher.group(3));
			list.add(param);
		}
		return list;
	}

	private List<MyAnnotation> getMyAnnotation(String annoText){
		List<MyAnnotation> annos = new ArrayList<MyAnnotation>();
		if(annoText==null||annoText.trim().isEmpty()){
			return annos;
		}
		
		Matcher amatcher = this.getMatcher(annoText, REG_ANNO);
		while(amatcher.find()){
			MyAnnotation anno = new MyAnnotation();
			anno.setName(amatcher.group(1));
			Map<String,Object> properties = new LinkedHashMap<String,Object>();
			anno.setParameters(properties);
			String params = amatcher.group(2);
			if(params!=null){
				if(params.startsWith("\"") && params.endsWith("\"")){
					properties.put("value", params);
				}else if(params.startsWith("{") && params.endsWith("}")){
					List<String> vList = new ArrayList<String>();
					Matcher pmatcher = this.getMatcher(params, "\"[^\"]+\"");
					while(pmatcher.find()){
						vList.add(pmatcher.group(0));
					}
					properties.put("value", vList);
				}else{
					Matcher pmatcher = this.getMatcher(params, REG_KEYVALUE);
					while(pmatcher.find()){
						properties.put(pmatcher.group(1), pmatcher.group(2));
					}
				}
			}
			annos.add(anno);
		}
		return annos;
	}

	private List<String> getMyImportList(){
		List<String> importList = new ArrayList<String>();
		Matcher matcher = this.getMatcher(sourceCode, REG_IMPORT);
		while(matcher.find()){
			String clsName = matcher.group(1);
			importList.add(clsName);
		}
		return importList;
	}

	protected Map<String,String> getMyImportMap(){
		Map<String, String> importMap = new HashMap<String,String>();
		List<String> importList = this.getMyImportList();
		for(String clsName:importList){
			String simpleName = _getSimpleName(clsName);
			importMap.put(simpleName, clsName);
		}
		return importMap;
	}

	protected String getFullTypeName(String typeName){
		if(typeName==null){
			return null;
		}
		if(ModelDataType.isPrimitive(typeName)){
			return typeName;
		}
		if(typeName.indexOf('.')!=-1){
			return typeName;
		}
		
		if("void".equals(typeName)){
			return typeName;
		}
		if("Object".equals(typeName)){
			return "java.lang."+typeName;
		}
	
		String rawtype = "";
		Matcher matcher = this.getMatcher(typeName, "(\\w+)<([^>]+)>");
		if(matcher.matches()){
			typeName = matcher.group(1);
			rawtype = matcher.group(2);
			rawtype = this.getFullTypeName(rawtype);
			rawtype = '<'+rawtype+'>';
		}
		
		String fullName = this.getMyImportMap().get(typeName);
		if(fullName!=null){
			fullName = fullName+rawtype;
			return fullName;
		}
		MyPackage myPackage = this.getMyPackage();
		if(myPackage!=null){
			return myPackage.getName()+'.'+typeName;
		}
		return typeName;
	}

	private String _getSimpleName(String name){
		int pos = name.lastIndexOf('.');
		if(pos==-1){
			return name;
		}
		return name.substring(pos+1);
	}
	
}
